package org.joot.jmir2.util;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.joot.jmir2.exception.JMir2Exception;

/**
 * WIL读取器
 * <br>
 * 对于多线程，此方法也不会造成阻塞
 * <br>
 * <b>请勿自行使用此类函数，因为函数返回结果并未存于全局缓存({@link AllCache#getImageInfo(Integer) cache})</b>如果您使用本类函数，请将结果加入全局缓存({@link AllCache#putImageInfo(Integer, WILImageInfo) cache})
 * 
 * @author ShawRyan
 */
public final class Wils {
	/** log */
	private static Logger log = Logger.getLogger(Wils.class);
	
	/**
	 * 从内部(与class文件同级文件夹，如jar包内，bin文件夹内)获取某个图片库中特定图片
	 * 
	 * @param index
	 * 		图片在图片库中的索引
	 * @param wilPath
	 * 		图片库相对路径
	 * @return
	 * 		图片信息
	 */
	public static WILImageInfo get(int index, String wilPath) {
		String wixPath = wilPath.substring(0, wilPath.length() - 4) + ".wix";
		File f_wil = Files.file(wilPath);
		File f_wix = Files.file(wixPath);
		if(f_wil == null || f_wix == null || !f_wil.exists() || !f_wix.exists()) return null;
		return get(new int[]{index}, Files.randomAccessFile(wilPath), Files.randomAccessFile(wixPath))[0];
	}
	
	/**
	 * 从内部(与class文件同级文件夹，如jar包内，bin文件夹内)获取某个图片库中所有图片
	 * 
	 * @param wilPath
	 * 		图片库文件全路径(相对路径)
	 * @return 图片库中图片信息集合
	 */
	public static WILImageInfo[] getAll(String wilPath) {
		String wixPath = wilPath.substring(0, wilPath.length() - 4) + ".wix";
		File f_wil = Files.file(wilPath);
		File f_wix = Files.file(wixPath);
		if(f_wil == null || f_wix == null || !f_wil.exists() || !f_wix.exists()) return null;
		return get(null, Files.randomAccessFile(wilPath), Files.randomAccessFile(wixPath));
	}
	
	/**
	 * 从内部(与class文件同级文件夹，如jar包内，bin文件夹内)获取某个图片库中特定图片
	 * 
	 * @param idxs
	 * 		图片索引集合(不要求有序或连续)
	 * @param wilPath
	 * 		图片库文件全路径(相对路径)
	 * @return 图片库中图片信息集合
	 */
	public static WILImageInfo[] getRange(int[] idxs, String wilPath) {
		String wixPath = wilPath.substring(0, wilPath.length() - 4) + ".wix";
		File f_wil = Files.file(wilPath);
		File f_wix = Files.file(wixPath);
		if(f_wil == null || f_wix == null || !f_wil.exists() || !f_wix.exists()) return null;
		return get(idxs, Files.randomAccessFile(wilPath), Files.randomAccessFile(wixPath));
	}
	
	/**
	 * 从raf_wil流中读取idxs索引数组代表的所有图片信息
	 * <br>
	 * 此方法不会对传入的流进行非空/非关闭判定
	 * <br>
	 * 也不会对其进行关闭
	 * 
	 * @param idxs
	 * 		索引数组
	 * @param raf_wil
	 * 		图片库流
	 * @param raf_wix
	 * 		图片库索引流
	 * @return 如果idxs为null，则返回所有图片信息，反之则返回个数为idxs长度的图片信息数组
	 */
	private static WILImageInfo[] get(int[] idxs, RandomAccessFile raf_wil, RandomAccessFile raf_wix) {
		try {
			WILImageInfo[] imageInfos = null;
			if(idxs != null) {
				imageInfos = new WILImageInfo[idxs.length];
			}
			byte[] bytesInt = null;
			int colorCount = 0;
			int verFlag = 0;
			raf_wil.skipBytes(48);// 跳过标题/图片数
			bytesInt = new byte[4];
			raf_wil.read(bytesInt);
			colorCount = Misc.colorCountToBitCount(Misc.readInt(bytesInt, 0, true));
			if (colorCount < 16) {
				// 8位灰度图可能版本标识不为0，此时操作不一样
				raf_wil.skipBytes(4); // 调色板使用默认
				raf_wil.read(bytesInt);
				verFlag = Misc.readInt(bytesInt, 0, true); // 版本标识
			}
			// 读取wix
			raf_wix.skipBytes(44); // 跳过标题
			raf_wix.read(bytesInt);
			int indexCount = Misc.readInt(bytesInt, 0, true);
			if (verFlag != 0) {
				raf_wix.skipBytes(4); // 版本标识不对需要多跳过标识的4字节
			}
			List<Integer> l_offsets = new ArrayList<Integer>();
			List<Integer> lengths = new ArrayList<Integer>();
			List<Integer> positions = new ArrayList<Integer>();
			if(imageInfos == null) {
				// 读取全部
				imageInfos = new WILImageInfo[indexCount];
				int[] a_offsets = new int[indexCount];
				for (int i = 0; i < indexCount; ++i) {
					raf_wix.read(bytesInt);
					a_offsets[i] = Misc.readInt(bytesInt, 0, true);
				}
				for (int i = 0; i < indexCount; ++i) {
					int length = (int) (i == indexCount - 1 ? (raf_wil.length() - a_offsets[i]) : (a_offsets[i + 1] - a_offsets[i]));
					l_offsets.add(a_offsets[i]);
					lengths.add(length);
					positions.add(i);
				}
			} else {
				// 读取特定图片
				int[] a_offsets = new int[idxs.length];
				int[] b_offsets = new int[idxs.length]; // 为了获取图片数据长度，读取下一个图片数据起始位置相减
				for (int i = 0; i < idxs.length; ++i) {
					int position = idxs[i] * 4 + 48; // 跳过索引前图片/标题/图片数
					if (verFlag != 0) {
						position += 4; // 版本标识不对需要多跳过标识的4字节
					}
					raf_wix.seek(position);
					raf_wix.read(bytesInt);
					a_offsets[i] = Misc.readInt(bytesInt, 0, true);
					if(idxs[i] == indexCount - 1) {
						b_offsets[i] = (int) raf_wil.length();
					} else {
						raf_wix.read(bytesInt);
						b_offsets[i] = Misc.readInt(bytesInt, 0, true);
					}
				}
				for (int i = 0; i < idxs.length; ++i) {
					l_offsets.add(a_offsets[i]);
					lengths.add(b_offsets[i] - a_offsets[i]);
					positions.add(i);
				}
			}
			Integer[] offsetList = l_offsets.toArray(new Integer[0]);
			Integer[] lengthList = lengths.toArray(new Integer[0]);
			int imageCount = offsetList.length;
			// 读取imgeinfo
			imageInfos = new WILImageInfo[imageCount];
			for (int i = 0; i < imageCount; ++i) {
				imageInfos[i] = read(offsetList[i], raf_wil, colorCount);
				imageInfos[i].index = positions.get(i).shortValue();
				imageInfos[i].dataStart = offsetList[i];
				imageInfos[i].dataSize = lengthList[i];
			}
			return imageInfos;
		} catch (IOException e) {
			JMir2Exception exception = JMir2Exception.valueOf(e, JMir2Exception.EXCEPTION_TYPE_FILESTREAM);
			log.error(exception.getMessage(), exception);
			throw exception;
		}
	}
	
	/** 从随机文件访问流中读取ImageInfo对象 */
	private static WILImageInfo read(int position, RandomAccessFile raf_wil, int colorCount) {
		try {
			raf_wil.seek(position);
			WILImageInfo res = new WILImageInfo();
			res.colorCount = (byte) colorCount;
			byte[] bytes = new byte[8];
			raf_wil.readFully(bytes);
			res.setWidth(Misc.readShort(bytes, 0, true));
			res.setHeight(Misc.readShort(bytes, 2, true));
			res.setOffsetX(Misc.readShort(bytes, 4, true));
			res.setOffsetY(Misc.readShort(bytes, 6, true));
			return res;
		} catch (IOException ex) {
			JMir2Exception exception = JMir2Exception.valueOf(ex, JMir2Exception.EXCEPTION_TYPE_FILESTREAM);
			log.error(exception.getMessage(), ex);
			throw exception;
		}
	}

	/** 图片信息 */
	public static class WILImageInfo {
		/** 图片宽度 */
		private short width;
		/** 图片高度 */
		private short height;
		/** 图片横向偏移量 */
		private short offsetX;
		/** 图片纵向偏移量 */
		private short offsetY;
		/** 图片数据在库中起始位置 */
		private int dataStart;
		/** 图片数据大小 */
		private int dataSize;
		/** 图片在库中的索引 */
		private short index;
		/** 图片色深 */
		private byte colorCount;

		/** 无参构造函数 */
		public WILImageInfo() {
		}

		/** 基于已有对象构造实例 */
		public WILImageInfo(WILImageInfo imageInfo) {
			this.height = imageInfo.height;
			this.offsetX = imageInfo.offsetX;
			this.offsetY = imageInfo.offsetY;
			this.width = imageInfo.width;
			this.dataStart = imageInfo.dataStart;
			this.dataSize = imageInfo.dataSize;
		}

		/** 带全部参数的构造函数 */
		public WILImageInfo(short width, short height, short offsetX,
				short offsetY, int dataStart, int dataSize) {
			this.width = width;
			this.height = height;
			this.offsetX = offsetX;
			this.offsetY = offsetY;
			this.dataStart = dataStart;
			this.dataSize = dataSize;
		}

		/** 获取图片宽度 */
		public short getWidth() {
			return width;
		}

		/** 设置图片高度 */
		public void setWidth(short width) {
			this.width = width;
		}

		/** 获取图片高度 */
		public short getHeight() {
			return height;
		}

		/** 设置图片高度 */
		public void setHeight(short height) {
			this.height = height;
		}

		/** 获取图片横线偏移量 */
		public short getOffsetX() {
			return offsetX;
		}

		/** 设置图片横向偏移量 */
		public void setOffsetX(short offsetX) {
			this.offsetX = offsetX;
		}

		/** 获取图片纵向偏移量 */
		public short getOffsetY() {
			return offsetY;
		}

		/** 设置图片纵向偏移量 */
		public void setOffsetY(short offsetY) {
			this.offsetY = offsetY;
		}

		/** 获取图片数据起始位置 */
		public int getDataStart() {
			return this.dataStart;
		}

		/** 设置图片数据起始位置 */
		public void setDataStart(int dataStart) {
			this.dataStart = dataStart;
		}

		/** 获取图片数据大小 */
		public int getDataSize() {
			return this.dataSize;
		}

		/** 设置图片数据大小 */
		public void setDataSize(int dataSize) {
			this.dataSize = dataSize;
		}

		/** 获取图片在库中的索引 */
		public short getIndex() {
			return this.index;
		}

		/** 获取图片色深 */
		public byte getColorCount() {
			return this.colorCount;
		}
	}
}